路由属性及方法
这里的history并不是window.history,而是React-Router的history。
这里以BrowserRouter举例,以Router指代BrowserRouter。
在ReactRouter中,路由配置都是包含在Router组件中的。如果APP的组件需要接收路由信息,那么都应该放在Route里。同一个APP不应该包含多个Router,看下例:
import React from 'react';
import ReactDOM from 'react-dom';
import {Link,BrowserRouter as Router,Route} from 'react-router-dom';
class MyApp extends React.Component{
render(){
return(
<div>
<Router>
<div>
<Link to="/" children="home" />
<Link to="/about" children="about" />
<Route exact path="/" render={(props)=>{return <h1>home</h1>}} />
<Route exact path="/about" render={(props)=>{return <h1>about</h1>}} />
</div>
</Router>
<Router>
<div>
<Link to="/" children="home" />
<Link to="/about" children="about" />
<Route exact path="/" render={(props)=>{return <h1>home</h1>}} />
<Route exact path="/about" render={(props)=>{return <h1>about</h1>}} />
</div>
</Router>
</div>
);
}
}
ReactDOM.render(<MyApp/>,document.querySelector("#root"));
上下俩个Router都配置了相同的路由,但并不会互相同步,彼此是隔离的,这也符合React的封装性。
Router管理路由,路由属性会由Route组件传递给每一个子组件。所以上例,切换路由Link组件是不会再次渲染的,也无法获取到路由信息。
路由属性
完整的例子:
import React from 'react';
import ReactDOM from 'react-dom';
import {Link,BrowserRouter as Router,Route,Switch} from 'react-router-dom';
class Home extends React.Component{
render(){
return(
<div>
<h1>Home</h1>
<section>{JSON.stringify(this.props.history)}</section>
</div>
)
}
}
class About extends React.Component{
render(){
return(
<div>
<h1>Home</h1>
<section>{JSON.stringify(this.props.history)}</section>
</div>
)
}
}
class RouteLink extends React.Component{
render(){
return(
<Route exact path={this.props.to} children={(props)=>{
return(
<div style={{color:props.match?'red':''}}>
<Link to={this.props.to} children={this.props.children}/>
</div>
);
}} />
);
}
}
class MyApp extends React.Component{
render(){
return(
<Router>
<div>
<RouteLink to="/" children="home" />
<RouteLink to="/about" children="about" />
<Route exact path="/" component={Home} />
<Route exact path="/about" component={About} />
</div>
</Router>
);
}
}
ReactDOM.render(<MyApp/>,document.querySelector("#root"));
定义了俩个路由Home和About,封装了Link组件RouteLink。注意RouteLink组件中以Route包装了Link,这里的Route因为要一直渲染,所以组件方法要用children而不能是component、render;同时精确匹配要配置exact。
history属性
挂载在Route子组件的props上。
参考上例,可以看到history包含如下属性:
- length:历史栈长度,最大为50(原生js就是最大50)
- action:类型,入栈就是PUSH,出栈就是POP,替换就是REPLACE
- location:location属性,包含5个熟悉:
- hash:hash
- key:默认6位随机数字字母组合字符串(浏览器重装不会生成该属性,只有路由跳转才会生成)
- pathname:访问路径
- search:包含?的查询字符串
- state:默认undefined,可以在跳转配置该参数,传递一个对象,用来辅助传递数据
location属性
挂载在Route子组件的props上。
等价于history的location属性。
match属性
挂载在Route子组件的props上。
如果当前路由不匹配,那么该属性值为null,所以你可以看到上例,添加背景色就是以此来判断的。
match属性包含如下属性:
- isExact:是否精确匹配
- path:Route配置path
- url:访问路径
- params:参数对象(路由是/:id,那么值就是{id:'xxx'})
history.push()
该方法会向历史栈添加一条记录,同时跳转到指定路由。
第一种用法:第一个参数为url,第二个参数为object用来设置state
第二种用法:第一个参数为object,配置具体的pathname、hash、search、state等
this.props.history.push("/about#hash?name=geralt",{from:"/"})
//等价于
this.props.history.push({
pathname:"/about",
hash:"#hash",
search:"?name=geralt",
state:{
from:"/"
}
})
history.replace()
该方法会用新的路由代替当前路由,并跳转。
用法同push()一样。
history.go(n)、history.goBack()、history.goForward()
go方法传递一个整数数字,正数代表前进,负数代表后退,不传或传0代表刷新。
go(1)
//等价于
goForward()
go(-1)
//等价于
goBack()
这三个方法都会导致页面重载。
history.block()
该方法实际上就是Prompt组件的函数式实现。在Prompt组件中,message可以是字符串也可以是函数,在block方法中也一样,可以传递一个字符串,也可以传递一个函数。
同时该方法会返回一个新方法,调用该方法将销毁window.confirm()注册的提示。
//我们改写之前的Home组件
class Home extends React.Component{
componentDidMount(){
this.unblock = this.props.history.block((nextLocation,action)=>{
return "确认离开首页???"
})
}
componentWillUnmount(){
this.unblock();
}
render(){
return(
<div>
<h1>Home</h1>
<section>{JSON.stringify(this.props.history)}</section>
</div>
)
}
}
这样,当离开Home组件匹配的路由前就会出现confirm对话框,而离开"/about"路由则不会出现。
为了避免重复注册,要在卸载周期函数中销毁实例。
block方法如果传递函数,则第一个参数为下一个路由的location属性,第二个参数为action类型(PUSH、POP、REPLACE三者之一)。函数返回true,表示不提示直接跳转;函数返回false,表示不提示阻止跳转。
可以看出来,该方法要比Prompt组件灵活很多。
history.listen(func)
调用该方法将注册一个监听函数,当路由发生变化则触发该函数。
同block()方法一样,是全局的,即"/"路由组件注册了,那么在"/about"路由内跳转也会触发。同时调用该方法也会返回一个方法,执行该方法即可销毁注册的监听函数。
class Home extends React.Component{
componentDidMount(){
this.unlisten = this.props.history.listen((nextLocation)=>{
console.log(nextLocation);
})
}
componentWillUnmount(){
this.unlisten();
}
render(){
return(
<div>
<h1>Home</h1>
<section>{JSON.stringify(this.props.history)}</section>
</div>
)
}
}
history.createHref(func)
该方法也传递一个函数,函数接收一个参数location。该方法本身会返回当前路由的pathname。
官方文档并没有介绍该方法,所以暂时无法得知具体的使用场景。